package com.kymatrix.kycarbon.xsmd.common.tenant.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.kymatrix.kycarbon.xsmd.common.tenant.filter.TenantSecurityWebFilter;
import com.kymatrix.kycarbon.xsmd.common.constants.Constant;
import com.kymatrix.kycarbon.xsmd.common.mybatisplus.util.MyBatisUtils;
import com.kymatrix.kycarbon.xsmd.common.redis.config.CacheProperties;
import com.kymatrix.kycarbon.xsmd.common.tenant.aop.TenantIgnoreAspect;
import com.kymatrix.kycarbon.xsmd.common.tenant.db.TenantDatabaseInterceptor;
import com.kymatrix.kycarbon.xsmd.common.tenant.redis.TenantRedisCacheManager;
import com.kymatrix.kycarbon.xsmd.common.tenant.filter.TenantContextWebFilter;
import com.kymatrix.kycarbon.xsmd.system.api.service.TenantApi;
import java.util.Objects;
import javax.annotation.Resource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.BatchStrategies;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

@Component
@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true) 
@EnableConfigurationProperties(TenantProperties.class)
@Configuration
public class TenantAutoConfiguration {

    // ========== AOP ==========

    @Bean
    public TenantIgnoreAspect tenantIgnoreAspect() {
        return new TenantIgnoreAspect();
    }

    // ========== DB ==========
    
    @Resource
    private TenantProperties properties;

    @Bean
    public TenantLineInnerInterceptor tenantLineInnerInterceptor(MybatisPlusInterceptor interceptor) {
        TenantLineInnerInterceptor inner = new TenantLineInnerInterceptor(new TenantDatabaseInterceptor(properties));
        // 添加到 interceptor 中
        // 需要加在首个，主要是为了在分页插件前面。这个是 MyBatis Plus 的规定
        MyBatisUtils.addInterceptor(interceptor, inner, 0);
        return inner;
    }

    // ========== WEB ==========

    @Bean
    public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter() {
        FilterRegistrationBean<TenantContextWebFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new TenantContextWebFilter());
        registrationBean.setOrder(Constant.WebFilterOrderConstants.TENANT_CONTEXT_FILTER);
        return registrationBean;
    }

    // ========== Security ==========
    
    @Bean
    public FilterRegistrationBean<TenantSecurityWebFilter> tenantSecurityWebFilter(TenantProperties tenantProperties,
                                                                                   TenantApi tenantApi ) {
        FilterRegistrationBean<TenantSecurityWebFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new TenantSecurityWebFilter(tenantProperties, tenantApi));
        registrationBean.setOrder(Constant.WebFilterOrderConstants.TENANT_SECURITY_FILTER);
        return registrationBean;
    }


    // ========== Redis ==========
    @Resource
    private RedisCacheConfiguration redisCacheConfiguration;

    @Resource
    private RedisTemplate redisTemplate;

    @Bean
    @Primary // 引入租户时，tenantRedisCacheManager 为主 Bean
    public RedisCacheManager tenantRedisCacheManager() {
        // 创建 RedisCacheWriter 对象
        RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
        RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory,
                BatchStrategies.scan(CacheProperties.REDIS_SCAN_BATCH_SIZE_DEFAULT));
        // 创建 TenantRedisCacheManager 对象
        return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration);
    }

}
